package com.linser.gistools.utils;

import com.linser.gistools.refactoring.geotools.layer.FeatureLayerNew;
import javafx.scene.shape.Circle;
import javafx.scene.shape.Rectangle;
import javafx.scene.shape.Shape;
import org.geotools.coverage.grid.GridCoverage2D;
import org.geotools.data.DataStore;
import org.geotools.data.DataStoreFinder;
import org.geotools.data.FeatureSource;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureSource;
import org.geotools.data.store.ReprojectingFeatureCollection;
import org.geotools.gce.geotiff.GeoTiffReader;
import org.geotools.geometry.jts.ReferencedEnvelope;
import org.geotools.http.HTTPClientFinder;
import org.geotools.map.FeatureLayer;
import org.geotools.map.GridCoverageLayer;
import org.geotools.map.Layer;
import org.geotools.map.MapContent;
import org.geotools.ows.ServiceException;
import org.geotools.ows.wmts.WebMapTileServer;
import org.geotools.ows.wmts.map.WMTSMapLayer;
import org.geotools.ows.wmts.model.WMTSCapabilities;
import org.geotools.ows.wmts.model.WMTSLayer;
import org.geotools.styling.*;
import org.geotools.tile.TileService;
import org.geotools.tile.impl.osm.OSMService;
import org.geotools.tile.util.TileLayer;
import org.geotools.util.factory.Hints;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
import org.opengis.style.GraphicalSymbol;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.awt.*;
import java.io.*;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author: linser
 * @Desc:地图操作类
 * @create: 2024-01-21 15:36
 **/
public class MapOperation {
    private static final Logger logger = LoggerFactory.getLogger(MapOperation.class);

    /**
     * 添加shp图层
     * @param shpFile shp文件
     * @throws MalformedURLException
     */
    public static void addShpLayer(File shpFile) throws MalformedURLException {
        MapContent mapContent = GlobalVariables.mapContent;
        String fileName = shpFile.getName();
        String nameWithoutExtension = fileName.substring(0, fileName.lastIndexOf('.'));
        try {
            Map<String, Object> params = new HashMap<>();
            params.put("url", shpFile.toURI().toURL());
            params.put("create spatial index", false);
            params.put("memory mapped buffer", false);
            // 获取源数据的编码
            Charset currentFileCharset = getShapefileCharset(shpFile.getPath());
            params.put("charset", currentFileCharset.name());
            DataStore store = DataStoreFinder.getDataStore(params);
            SimpleFeatureSource featureSource = store.getFeatureSource(store.getTypeNames()[0]);
            // TODO 此处必须将源图层数据转换为目标图层投影 不然无法显示，但是SimpleFeatureCollection的类型将不是迭代器类型，性能会降低
            // 将源图层数据转换为目标图层投影
            SimpleFeatureCollection sourceFeatures = featureSource.getFeatures();
            SimpleFeatureCollection targetFeatures = new ReprojectingFeatureCollection(sourceFeatures, mapContent.getCoordinateReferenceSystem());

            addFeatureCollectionLayer(targetFeatures,nameWithoutExtension,currentFileCharset.name());
        }catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 添加tif图层
     * @param tifFile tif文件
     * @throws IOException
     */
    public static void addTifLayer(File tifFile) throws IOException {

        GeoTiffReader reader = new GeoTiffReader(tifFile, new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.TRUE));
        GridCoverage2D coverage = reader.read(null);
        String fileName = tifFile.getName();
        String nameWithoutExtension = fileName.substring(0, fileName.lastIndexOf('.'));
        // 添加TIF文件到地图
        GlobalVariables.mapContent.addLayer(new GridCoverageLayer(coverage,LayerStyleUtil.createRasterStyle(reader),nameWithoutExtension));
    }

    public static void addDbTableLayer(DataStore dataStore,String tablename ) throws IOException {
        MapContent mapContent = GlobalVariables.mapContent;
        //SimpleFeatureSource featureSource = dataStore.getFeatureSource(tablename);
        FeatureSource<SimpleFeatureType, SimpleFeature> featureSource = dataStore.getFeatureSource(tablename);
        Color randomColor = new Color((int) (Math.random() * 0x1000000));
        //Style style = SLD.createSimpleStyle(featureSource.getSchema(),randomColor);
        Style style = SLD.createPolygonStyle(randomColor,randomColor, 0.5F);

        Layer layer = new FeatureLayer(featureSource, style,tablename);
        mapContent.addLayer(layer);
    }

    public static void addFeatureCollectionLayer(SimpleFeatureCollection simpleFeatureCollection){
        String layerName = simpleFeatureCollection.getSchema().getName().toString();
        addFeatureCollectionLayer(simpleFeatureCollection, layerName);
    }
    public static void addFeatureCollectionLayer(SimpleFeatureCollection simpleFeatureCollection, String layerName){
        addFeatureCollectionLayer(simpleFeatureCollection,layerName,null);
    }
    public static void addFeatureCollectionLayer(SimpleFeatureCollection simpleFeatureCollection, String layerName, String charset){
        MapContent mapContent = GlobalVariables.mapContent;
        Color randomColor = new Color((int) (Math.random() * 0x1000000));
        Style style = SLD.createSimpleStyle(simpleFeatureCollection.getSchema(),randomColor);
        SimpleFeatureCollection targetFeatures = new ReprojectingFeatureCollection(simpleFeatureCollection, mapContent.getCoordinateReferenceSystem());
        FeatureLayerNew layer = new FeatureLayerNew(targetFeatures, style,layerName);
        if(charset != null && !charset.isEmpty()){
            Map<String, Object> customData = new HashMap<>();
            customData.put("charset", charset);
            layer.setCustomData(customData);
        }
        mapContent.addLayer(layer);
    }
    public static void addWmtsLayer(String url) throws IOException, ServiceException {
        MapContent mapContent = GlobalVariables.mapContent;
        // 定义地图服务的URL
            URL wmtsUrl = new URL(url);
//            // 创建一个 Map<String, String> 类型的参数
//            Map<String, String> headerParameters = new HashMap<>();
//            // 添加键值对
//            headerParameters.put("kt", "44312750c059d03010cde3b0c1905493");
            // 创建天地图服务对象
            WebMapTileServer tileServer = new WebMapTileServer(wmtsUrl, HTTPClientFinder.createClient());
            //功能和发布信息
            WMTSCapabilities capabilities = tileServer.getCapabilities();
            //capabilities.getRequest().getGetTile().setGet(wmtsUrl);
            //String serverName = capabilities.getService().getName();
            //String serverTitle = capabilities.getService().getTitle();
            //System.out.println("Capabilities retrieved from server: " + serverName + " (" + serverTitle + ")");
            List<WMTSLayer> wmtsLayers = capabilities.getLayerList();
            if(!wmtsLayers.isEmpty()){
                WMTSLayer wmtsLayer = wmtsLayers.get(0);
                WMTSMapLayer wmtsMapLayer = new WMTSMapLayer(tileServer, wmtsLayer);
                wmtsLayer.setTitle(wmtsLayer.getName());
                mapContent.addLayer(wmtsMapLayer);
            }
    }

    public static void addOsmLayer(String url) throws IOException {
        MapContent mapContent = GlobalVariables.mapContent;
        String baseURL = "http://tile.osm.org/";
        if(!url.isEmpty()){
            baseURL = url;
        }
        TileService service = new OSMService("OSM", baseURL);
        Layer osmLayer =new TileLayer(service);
        osmLayer.setTitle("OSM");
        mapContent.addLayer(osmLayer);
    }

    /**
     * 移除图层
     * @param layer
     */
    public static void removeLayer(Layer layer){
        MapContent mapContent = GlobalVariables.mapContent;
        mapContent.removeLayer(layer);
    }

    /**
     * 缩放到图层
     * @param layer
     */
    public static void zoomToLayer(Layer layer){
        ReferencedEnvelope envelope = layer.getBounds();

        // 创建一个新的 Envelope 对象，用于设置视口范围
        ReferencedEnvelope newBounds = new ReferencedEnvelope(envelope);
        MapContent mapContent = GlobalVariables.mapContent;

        // 设置视口范围为新的 Envelope 对象，并启用宽高比匹配
//        mapContent.getViewport().setBounds(newBounds);
//        mapContent.getViewport().setMatchingAspectRatio(true);
        //强制刷新
        GlobalVariables.jMapPane.setDisplayArea(newBounds);
        GlobalVariables.jMapPane.reset();
        GlobalVariables.jMapPane.repaint();
    }

    public static Shape getLayerGraphicNode(Layer layer){
        Style style = layer.getStyle();
        if (style != null) {
            List<FeatureTypeStyle> featureTypeStyles = style.featureTypeStyles();
            for (FeatureTypeStyle featureTypeStyle : featureTypeStyles) {
                List<Rule> rules = featureTypeStyle.rules();
                for (Rule rule : rules) {
                    Symbolizer[] symbolizers = rule.getSymbolizers();
                    for (Symbolizer symbolizer : symbolizers) {
                        if (symbolizer instanceof PolygonSymbolizer) {
                            Fill fill = ((PolygonSymbolizer) symbolizer).getFill();
                            if (fill != null && fill.getColor() != null) {
                                return new Rectangle(20, 20, ColorOperation.fxColorFromAwtColor(SLD.color(fill)));
                                //return SLD.color(fill);
                            }
                        }else if(symbolizer instanceof PointSymbolizer){
                            Graphic graphic = ((PointSymbolizerImpl) symbolizer).getGraphic();
                            //double graphicSize = (double) graphic.getSize().toString();
                            double graphicSize = 10;
                            List<GraphicalSymbol> graphicalSymbols = graphic.graphicalSymbols();
                            if(!graphicalSymbols.isEmpty()){
                                Mark mark = (Mark) graphicalSymbols.get(0);
                                Color fillColor = SLD.color(mark.getFill());
                                Color borderColor = SLD.color(mark.getStroke());
                                double strokeWidth = SLD.width(mark.getStroke());
                                //double strokeWidth = (Double) mark.getStroke().getWidth();
                                Shape shape;
                                if (mark.getWellKnownName().toString().equals("Circle")) {
                                    Circle circle = new Circle(graphicSize / 2);
                                    circle.setCenterX(graphicSize / 2 );
                                    circle.setCenterY(graphicSize / 2);
                                    circle.setFill(ColorOperation.fxColorFromAwtColor(fillColor));
                                    circle.setStroke(ColorOperation.fxColorFromAwtColor(borderColor));
                                    circle.setStrokeWidth(strokeWidth);
                                    shape = circle;
                                } else {
                                    shape = new Rectangle(graphicSize, graphicSize, ColorOperation.fxColorFromAwtColor(fillColor));
                                }
                                return shape;
                            }
                        }
                    }
                }
            }
        }
        return null; // 如果找不到填充色，返回null或者默认颜色
    }

    public static Charset getShapefileCharset(String shapefilePath) throws IOException {
        String encoding = "GBK"; // 默认编码 UTF-8
        // 读取 .cpg 文件内容
        String cpgFilePath = shapefilePath.replace(".shp", ".cpg");

        if (!Files.exists(Paths.get(cpgFilePath))) {
            String dbfFilePath = shapefilePath.replace(".shp", ".dbf");
            if (Files.exists(Paths.get(dbfFilePath))){
                encoding = DataOperation.getCharset(dbfFilePath);
                logger.warn("找不到shapeFile的cpg文件！暂时使用dbf文件的猜想编码字符");
            }

        }else {
            try (BufferedReader reader = new BufferedReader(new FileReader(cpgFilePath))) {
                String line;
                while ((line = reader.readLine()) != null) {
                    encoding = line.trim();
                    break; // 只读取第一行
                }
            } catch (IOException e) {
                System.out.println("读取cpg文件时出错：" + e.getMessage());
            }
        }
        // 返回字符编码对象
        return Charset.forName(encoding);
    }

    /**
     * 创建shp文件的cpg文件
     * @param shapefilePath shp文件路径
     * @param charset 编码格式
     * @throws IOException
     */
    public static void creatCpgFile(String shapefilePath, String charset) throws IOException {
        String cpgFilePath = shapefilePath.replace(".shp", ".cpg");
        if (!Files.exists(Paths.get(cpgFilePath))) {
            FileOutputStream cpgFileOutputStream = new FileOutputStream(cpgFilePath);
            cpgFileOutputStream.write(charset.getBytes());
            cpgFileOutputStream.close();
        }
    }

}
